package com.cmc.cloud.cmclink.doc.task;

import java.util.*;
import java.util.stream.Collectors;

import javax.annotation.Resource;

import com.cmc.cloud.cmclink.doc.vo.sovo.sovslvoyvo.SoVslvoyApproveSpaceVO;import org.springframework.stereotype.Component;

import com.alibaba.nacos.client.naming.utils.CollectionUtils;
import com.cmc.cloud.cmclink.doc.api.document.emums.ApproveStatusEnum;
import com.cmc.cloud.cmclink.doc.api.document.emums.CargoTypeEnum;
import com.cmc.cloud.cmclink.doc.api.document.emums.CntrTypeEnum;
import com.cmc.cloud.cmclink.doc.api.document.emums.SpaceTypeCodeEnum;
import com.cmc.cloud.cmclink.doc.constants.CommonConstants;
import com.cmc.cloud.cmclink.doc.entity.*;
import com.cmc.cloud.cmclink.doc.enums.filter.FilterConditionFieldEnum;
import com.cmc.cloud.cmclink.doc.service.VoyageBsaCbaDetailService;
import com.cmc.cloud.cmclink.doc.service.VoyageBsaCbaService;
import com.cmc.cloud.cmclink.doc.service.impl.so.BizSoServiceImpl;
import com.cmc.cloud.cmclink.doc.service.impl.so.SoCargoServiceImpl;
import com.cmc.cloud.cmclink.doc.service.so.SoForecastService;
import com.cmc.cloud.cmclink.doc.service.so.SoService;
import com.cmc.cloud.cmclink.doc.service.so.SoVslvoyService;
import com.cmc.cloud.cmclink.doc.util.so.CmclinkExpressionUtil;
import com.cmc.cloud.cmclink.doc.vo.voyagebsacbadetailvo.VoyageBsaCbaDetailRespVO;
import com.xxl.job.core.handler.annotation.XxlJob;

import lombok.extern.slf4j.Slf4j;

/**
 * SO自动批舱
 * 
 * @author dev89
 * @date 2025/07/31 11:25
 **/
@Component
@Slf4j
public class SoAutoApproveSpace {
    private static final String SO = "SO";
    private static final String CARGO = "Cargo";
    private static final String VSLVOY = "Vslvoy";
    private static final String FORECAST = "Forecast";
    @Resource
    private SoVslvoyService soVslvoyService;
    @Resource
    private SoForecastService soForecastService;
    @Resource
    private VoyageBsaCbaService voyageBsaCbaService;
    @Resource
    private SoService soService;
    @Resource
    private SoCargoServiceImpl soCargoService;
    @Resource
    private VoyageBsaCbaDetailService voyageBsaCbaDetailService;
    @Resource
    private BizSoServiceImpl bizSoServiceImpl;

    /**
     * 计算so所需teu及rf
     *
     * @param cargoListMap cargoListMap
     * @param rfTeuMap rfTeuMap
     * @param soTeuMap soTeuMap
     * @param soForecastDOList soForecastDOList
     */
    public static void transferSoTeu(List<SoForecastDO> soForecastDOList, Map<Long, Integer> soTeuMap,
        Map<Long, Integer> rfTeuMap, Map<Long, SoCargoDO> cargoListMap) {
        soForecastDOList.forEach(forecast -> {
            int cntrTeu = Integer.parseInt(forecast.getCntrSize()) / 20;
            long cntrQuantity = forecast.getCntrQuantity();
            int cntrQuantityInt = (int)cntrQuantity;
            int temTeu = cntrTeu * cntrQuantityInt;
            soTeuMap.compute(forecast.getSoId(), (k, v) -> v == null ? temTeu : v + temTeu);
            String cntrType = forecast.getCntrType();
            String cargoType = cargoListMap.getOrDefault(forecast.getSoId(), new SoCargoDO()).getCargoType();
            boolean check = (CntrTypeEnum.RF.getValue().equalsIgnoreCase(cntrType)
                || CntrTypeEnum.RH.getValue().equalsIgnoreCase(cntrType))
                && !CargoTypeEnum.DRY.getValue().equals(cargoType);
            if (check) {
                rfTeuMap.compute(forecast.getSoId(), (k, v) -> v == null ? cntrQuantityInt : v + cntrQuantityInt);
            }
        });
    }

    @XxlJob("approveSpaceHandler")
    public void autoApproveSpace() {
        log.info(">>>>>>>>>>> so auto approve space init.");
        // 获取可以自动批舱的船名航次
        List<SoVslvoyApproveSpaceVO> vslvoyList = soVslvoyService.selectByAutoApproveSpace();
        if (CollectionUtils.isEmpty(vslvoyList)) {
            return;
        }

        // 去重
        vslvoyList = vslvoyList.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(
            // 利用 TreeSet 的排序去重构造函数来达到去重元素的目的
            // 根据firstName去重
            () -> new TreeSet<>(Comparator.comparing(SoVslvoyApproveSpaceVO::getId))), ArrayList::new));

        // 更新状态为已自动批舱进行中
        List<SoVslvoyDO> soVslvoyDOList = new ArrayList<>();
        vslvoyList.forEach(vslvoy -> {
            vslvoy.setInAutoApproveTrack(true);
            soVslvoyDOList.add(vslvoy);
        });
        soVslvoyService.updateBatchDO(soVslvoyDOList);

        // 分配舱位
        allocateSpace(vslvoyList);

        // 解除自动批舱进行中
        soVslvoyDOList.forEach(vslvoy -> {
            vslvoy.setInAutoApproveTrack(false);
            vslvoy.setAutoApproceProcessFlag(true);
        });
        soVslvoyService.updateBatchDO(soVslvoyDOList);
    }

    private void allocateSpace(List<SoVslvoyApproveSpaceVO> vslvoyList) {
        List<Long> soIds =
            vslvoyList.stream().map(SoVslvoyApproveSpaceVO::getSoId).distinct().collect(Collectors.toList());
        List<Long> voyageBsaIds =
            vslvoyList.stream().map(SoVslvoyApproveSpaceVO::getVoyageBsaId).distinct().collect(Collectors.toList());
        List<SoForecastDO> soForecastDoList = soForecastService.seleteBySoIdList(soIds);

        List<SoDO> soList = soService.getSoList(soIds);
        Map<Long, SoDO> soDoMap = soList.stream().collect(Collectors.toMap(SoDO::getId, obj -> obj));
        Map<Long, List<SoForecastDO>> forecastListMap =
            soForecastDoList.stream().collect(Collectors.groupingBy(SoForecastDO::getSoId));
        Map<Long, SoCargoDO> cargoListMap = soCargoService.selectBySoIds(soIds);
        Map<Long, List<SoVslvoyDO>> vslvoyListMap = soVslvoyService.seleteBySoIdsMap(soIds);

        // 计算SO所需TEU,冷插数RF Map<soId, teu数量> <soId, rfUnit数量>
        Map<Long, Integer> soTeuMap = new HashMap<>(16);
        Map<Long, Integer> soRfMap = new HashMap<>(16);
        transferSoTeu(soForecastDoList, soTeuMap, soRfMap, cargoListMap);

        List<VoyageBsaCbaDO> voyageBsaCbaDOList = voyageBsaCbaService.getByMainIds(voyageBsaIds);

        // 分配预留舱位
        List<SoVslvoyDO> vslvoyResults = computeAllocateSpace(vslvoyList, soTeuMap, soRfMap, voyageBsaCbaDOList,
            soDoMap, forecastListMap, cargoListMap, vslvoyListMap);

        // 更新数据表
        updateToDb(voyageBsaCbaDOList, vslvoyResults);
    }

    private void updateToDb(List<VoyageBsaCbaDO> voyageBsaCbaDOList, List<SoVslvoyDO> vslvoyResults) {
        // 更新预留舱位
        if (!CollectionUtils.isEmpty(voyageBsaCbaDOList)) {
            voyageBsaCbaService.updateBatchVoyageBsaCba(voyageBsaCbaDOList);
        }

        // 更新vslvoy批舱状态
        if (!CollectionUtils.isEmpty(vslvoyResults)) {
            soVslvoyService.updateBatchDO(vslvoyResults);
        }
    }

    private List<SoVslvoyDO> computeAllocateSpace(List<SoVslvoyApproveSpaceVO> vslvoyList, Map<Long, Integer> soTeuMap,
        Map<Long, Integer> soRfMap, List<VoyageBsaCbaDO> voyageBsaCbaDOList, Map<Long, SoDO> soDoMap,
        Map<Long, List<SoForecastDO>> forecastListMap, Map<Long, SoCargoDO> cargoMap,
        Map<Long, List<SoVslvoyDO>> vslvoyListMap) {
        List<SoVslvoyDO> vslvoyResults = new ArrayList<>();
        for (SoVslvoyApproveSpaceVO vslvoy : vslvoyList) {
            // 判断是否有预留舱位可分配
            boolean allocateReverse = false;
            for (VoyageBsaCbaDO bsa : voyageBsaCbaDOList) {
                if (!SpaceTypeCodeEnum.RESERVE_SPACE.getValue().equals(bsa.getSpaceTypeCode())) {
                    continue;
                }

                VoyageBsaCbaDetailRespVO rsp = voyageBsaCbaDetailService.getByMainId(bsa.getId());
                long soId = vslvoy.getSoId();
                boolean checkRule = checkPreAllocationRule(rsp, soDoMap.get(soId),
                    forecastListMap.getOrDefault(soId, new ArrayList<>()), cargoMap.get(soId),
                    vslvoyListMap.getOrDefault(soId, new ArrayList<>()));
                if (checkRule) {
                    if (allocateRemainTeuAndRf(soTeuMap, soRfMap, bsa, vslvoy)) {
                        vslvoyResults.add(vslvoy);
                        allocateReverse = true;
                        break;
                    }
                }
            }

            if (allocateReverse) {
                continue;
            }

            // 预留舱位不够分配，判断开放舱位是否可分配
            for (VoyageBsaCbaDO bsa : voyageBsaCbaDOList) {
                if (!SpaceTypeCodeEnum.OPEN_SPACE.getValue().equals(bsa.getSpaceTypeCode())) {
                    continue;
                }

                if (allocateRemainTeuAndRf(soTeuMap, soRfMap, bsa, vslvoy)) {
                    vslvoyResults.add(vslvoy);
                    break;
                }
            }
        }
        return vslvoyResults;
    }

    private boolean checkPreAllocationRule(VoyageBsaCbaDetailRespVO rsp, SoDO soDo, List<SoForecastDO> forecastList,
        SoCargoDO soCargo, List<SoVslvoyDO> vslvoyList) {
        if (rsp == null || rsp.getFilterGroup() == null) {
            return true;
        }

        try {
            // 转换 拼接SO的信息 字段信息
            Map<String, List<String>> fieldMap = FilterConditionFieldEnum.getAllField();
            List<Map<String, Object>> vslvoyMapList = new ArrayList<>();
            List<Map<String, Object>> forecastMapList = new ArrayList<>();
            Map<String, Object> soMap = bizSoServiceImpl.invokeGetValue(soDo, fieldMap.get(SO));
            Map<String, Object> cargoMap = bizSoServiceImpl.invokeGetValue(soCargo, fieldMap.get(CARGO));
            vslvoyList.forEach(e -> {
                vslvoyMapList.add(bizSoServiceImpl.invokeGetValue(e, fieldMap.get(VSLVOY)));
            });
            forecastList.forEach(e -> {
                forecastMapList.add(bizSoServiceImpl.invokeGetValue(e, fieldMap.get(FORECAST)));
            });

            for (int j = 0; j < vslvoyMapList.size(); j++) {
                Map<String, Object> vslvoyMapTemp = vslvoyMapList.get(j);
                for (int k = 0; k < forecastMapList.size(); k++) {
                    Map<String, Object> forecastMapTemp = forecastMapList.get(k);
                    Map<String, Object> allTemp = new HashMap<>(CommonConstants.DEFAULT_MAP_COUNT);
                    allTemp.putAll(soMap);
                    allTemp.putAll(cargoMap);
                    allTemp.putAll(vslvoyMapTemp);
                    allTemp.putAll(forecastMapTemp);
                    boolean check = CmclinkExpressionUtil.check(rsp.getFilterGroup(), allTemp);
                    if (check) {
                        return true;
                    }
                }
            }
        } catch (Exception e) {
            log.error("caught an exception on auto space, checkPreAllocationRule", e);
        }

        return false;
    }

    private boolean allocateRemainTeuAndRf(Map<Long, Integer> soTeuMap, Map<Long, Integer> soRfMap, VoyageBsaCbaDO bsa,
        SoVslvoyApproveSpaceVO vslvoy) {
        int soTeu = soTeuMap.getOrDefault(vslvoy.getSoId(), 0);
        int soRf = soRfMap.getOrDefault(vslvoy.getSoId(), 0);
        int remainTeu = bsa.getRemainAllocationSpace();
        int remainRf = bsa.getRemainRfUnit();
        if (remainTeu >= soTeu && remainRf >= soRf) {
            remainTeu -= soTeu;
            remainRf -= soRf;
            bsa.setRemainAllocationSpace(remainTeu);
            bsa.setRemainRfUnit(remainRf);
            vslvoy.setSpaceApproveStatus(ApproveStatusEnum.APPROVED.getValue());
            vslvoy.setAllocationId(bsa.getId());
            return true;
        }
        return false;
    }
}
